# coding:utf-8
"""
集成电路精英挑战赛-带约束的智能版图拼接
参赛队： 探索小队
学校： 杭州电子科技大学
成员： 刘鑫(队长) 汪银辉 潘钇名
时间： 2021年12月12号
>>shapely==1.7.1 (版本)


copy -----> hd5_ga_v2
20211026
更改重心坐标为 ------> 各端点取平均值
删除模块字典多余信息
20211103
修改模块最短距离
更新评估函数 get_port_dis
20211104
更新不规则布局时的坐标及BL策略
20211110
更新模块重心坐标为最大矩形重心
20211126
测试用！！！！！
"""
import os
import sys
import copy
import re  # 用正则表达式提取坐标
import time
import random

import matplotlib

matplotlib.use('Agg')

import matplotlib.pyplot as plt

import numpy as np
import shapely.affinity
import shapely.ops

from shapely.geometry import Polygon


def trans_ndarray():
    for i1 in range(len(list_all)):  # 4次循环
        s = []
        for i2 in range(len(list_all[i1])):
            s.append(list(eval(list_all[i1][i2])))

        if i1 == 0:
            dic['boundary'] = s

        value_array = np.array(s)
        value_average = np.average(value_array, axis=0)
        dic[list_keys[i1] + '_centroid'] = np.round(value_average, 1).tolist()


def with_open():
    with open(file_connect_1, 'r', encoding='utf-8') as f:  # 提取connect数据 #
        data_str = f.readlines()  # 列表,整行数据b

        for i in range(len(data_str)):
            num_list = []
            num_list1 = []
            num_list2 = []
            if i % 3 == 0:
                num1 = re.findall('(?<=M).[0-9_]*', data_str[i + 1])
                num2 = re.findall('(?<=).[0-9_]*', data_str[i + 2])

                for j in range(len(num1)):
                    num_list1.append(eval(num1[j]))
                    num_list2.append(eval(num2[j]))
                    num_list = [num_list1, num_list2]

                module_link.append(num_list)

    with open(file_Module, 'r', encoding='utf-8') as f:
        data_str = f.readlines()  # 列表,整行数据
        n = len(data_str)
        global module_sum
        module_sum = int((n - 2) // 5)  # 模块总数
        global modules
        modules = [None] * module_sum
        global area_zb
        area_zb = []
        for i in range(n):
            global dic
            dic = {}
            if i == 0:
                area_list = re.findall(r'[(](.*?)[)]', data_str[0])
                for k in range(len(area_list)):
                    area_zb.append(list(eval(area_list[k])))
                global area_poly
                area_poly = area_zb + [area_zb[0]]
                area_zb = [Polygon(area_zb).bounds[0:2]] + [Polygon(area_zb).bounds[2:]]
                global area_rec
                area_rec = [area_zb[0], [area_zb[0][0], area_zb[1][1]], area_zb[1], [area_zb[1][0], area_zb[0][1]],
                            area_zb[0]]
                continue
            if i % 5 == 2:
                module_name = data_str[i][8:-1]
                boundary = re.findall(r'[(](.*?)[)]', data_str[i + 1])
                port1 = re.findall(r'[(](.*?)[)]', data_str[i + 2])
                port2 = re.findall(r'[(](.*?)[)]', data_str[i + 3])
                port3 = re.findall(r'[(](.*?)[)]', data_str[i + 4])
                global list_all
                list_all = [boundary, port1, port2, port3]
                dic['module_name'] = eval(module_name)
                trans_ndarray()
                dic['module_max_bounds'] = [Polygon(dic['boundary']).bounds[0:2]] + [
                    Polygon(dic['boundary']).bounds[2:]]
                dic['boundary_centroid'] = [
                    round((dic['module_max_bounds'][1][0] + dic['module_max_bounds'][0][0]) / 2, 1),
                    round((dic['module_max_bounds'][1][1] + dic['module_max_bounds'][0][1]) / 2, 1)]
                dic['flag'] = 0
                modules[eval(module_name) - 1] = dic  # 按模块名顺序写入数据


def draw_module(modules_dic, *args, a=0):  # 可视化
    if args is None:
        m = 0
    else:
        m = 1
    area_zb_nda = np.array(area_poly)  # list转成ndarray
    x = list(area_zb_nda[:, 0])  # a的第一列
    y = list(area_zb_nda[:, 1])
    plt.plot(x, y, color='black', linewidth=3)
    if m:
        for j in range(len(args)):  # 循环打印模块
            for p in args:
                poly_m = modules_dic[p - 1]['boundary']
                poly_m.append(poly_m[0])
                boundary_xy_nda = np.array(poly_m)
                x = list(boundary_xy_nda[:, 0])
                y = list(boundary_xy_nda[:, 1])
                plt.plot(x, y, color='blue', linewidth=1.5)
    if a:
        for j in range(module_sum):  # 循环打印模块
            poly_m = modules_dic[j]['boundary']
            poly_m.append(poly_m[0])
            boundary_xy_nda = np.array(poly_m)
            x = list(boundary_xy_nda[:, 0])
            y = list(boundary_xy_nda[:, 1])
            plt.plot(x, y, color='blue', linewidth=1.5)

    s = file_out[:-3]

    plt.savefig(s + 'jpg')


class GA_SA:
    def __init__(self, modules_dir, area_rec_poly):
        self.modules_dir = copy.deepcopy(modules_dir)
        self.area_rec = area_rec_poly
        self.area_poly = Polygon(self.area_rec)
        self.M = len(modules_dir) // 2
        self.t = 0  # 初始化进化代数
        self.wall = 30  # 各模块间隔
        self.group = []
        self.group_value = []
        self.rec_init()  # 多边形外扩
        self.random_group()  # 产生随机群体

    def random_group(self):  # 随机产生初始群体
        for i in range(self.M):
            mdir_wall = copy.deepcopy(self.modules_dir)
            for j in range(module_sum):
                r = random.randint(0, 7)
                self.renew_module(mdir_wall, j + 1, r)
            random.shuffle(mdir_wall)  # 随机打乱列表里元素顺序
            self.group.append(mdir_wall)

    def crossover(self, idx):  # 交叉
        for i in range(self.M):
            if i == idx:
                continue
            gene = []

            if max(self.group_value) - min(self.group_value):
                p_c = (self.group_value[i] - min(self.group_value)) / (max(self.group_value) - min(self.group_value))
                rand = random.random()
                if p_c > rand:  # 开始个体交叉操作
                    r = random.randint(1, module_sum - 1)
                    if r == module_sum:
                        continue
                    group_i_new = self.group_best[:r]
                    for j in range(r):
                        gene.append(self.group_best[j]['module_name'])
                    for k in range(module_sum):
                        if self.group[i][k]['module_name'] not in gene:
                            group_i_new.append(self.group[i][k])
                    self.group[i] = group_i_new

    def mutation(self, idx):  # 变异
        for i in range(self.M):
            p_m = (self.group_value[i] - min(self.group_value)) / (
                        max(self.group_value) - min(self.group_value) + 0.001)
            rand = random.random()
            if i == idx:
                continue
            if p_m > rand:  # 开始个体变异操作
                for j in range(module_sum):
                    r = random.randint(0, 7)
                    self.renew_module(self.group[i], j + 1, r)
                random.shuffle(self.group[i])  # 随机打乱列表里元素顺序

    def renew_group(self):  # 更新群体
        self.group_value = []
        for i in range(self.M):
            self.move_bl(self.group[i])
            self.group_value.append(round(self.get_port_dis(self.group[i]), 1))

    def renew_module(self, mdir_wall, mdir_num, flag):
        if flag == 0:
            self.r0(mdir_wall, mdir_num)
        elif flag == 1:
            self.r90(mdir_wall, mdir_num)
        elif flag == 2:
            self.r180(mdir_wall, mdir_num)
        elif flag == 3:
            self.r270(mdir_wall, mdir_num)
        elif flag == 4:
            self.mx(mdir_wall, mdir_num)
        elif flag == 5:
            self.mxr90(mdir_wall, mdir_num)
        elif flag == 6:
            self.my(mdir_wall, mdir_num)
        else:
            self.myr90(mdir_wall, mdir_num)

    def r0(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])

    def r90(self, mdir_wall, mdir_num):  # , n=None
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                value_array = np.array(mdir_wall[i]['boundary'])
                xy_centroid = np.array(mdir_wall[i]['boundary_centroid']).reshape(1, 2)
                value = (xy_centroid - value_array) @ np.array([[0, -1], [1, 0]]) + xy_centroid
                mdir_wall[i]['boundary'] = np.round(value, 1).tolist()
                for j in range(3):
                    value_array = np.array(mdir_wall[i][modules_keys[j]]).reshape(1, 2)
                    value = (xy_centroid - value_array) @ np.array([[0, -1], [1, 0]]) + xy_centroid
                    mdir_wall[i][modules_keys[j]] = np.round(value.reshape(-1), 1).tolist()
                mdir_wall[i]['flag'] = 1
                mdir_wall[i]['module_max_bounds'] = [Polygon(mdir_wall[i]['boundary']).bounds[0:2]] + \
                                                    [Polygon(mdir_wall[i]['boundary']).bounds[2:]]

    def r180(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                for j in range(2):
                    self.r90(mdir_wall, mdir_num)
                mdir_wall[i]['flag'] = 2

    def r270(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                for j in range(3):
                    self.r90(mdir_wall, mdir_num)
                mdir_wall[i]['flag'] = 3

    def mx(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                value_array = np.array(mdir_wall[i]['boundary'])
                value_array[:, 1] = -value_array[:, 1] + 2 * mdir_wall[i]['boundary_centroid'][1]
                value = np.round(value_array, 1).tolist()
                mdir_wall[i]['boundary'] = value
                for j in range(3):
                    value = mdir_wall[i][modules_keys[j]][1]
                    value = round(2 * mdir_wall[i]['boundary_centroid'][1] - value, 1)
                    mdir_wall[i][modules_keys[j]][1] = value
                mdir_wall[i]['flag'] = 4
                mdir_wall[i]['module_max_bounds'] = [Polygon(mdir_wall[i]['boundary']).bounds[0:2]] + \
                                                    [Polygon(mdir_wall[i]['boundary']).bounds[2:]]

    def mxr90(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                self.mx(mdir_wall, mdir_num)
                self.r90(mdir_wall, mdir_num)
                mdir_wall[i]['flag'] = 5
                mdir_wall[i]['module_max_bounds'] = [Polygon(mdir_wall[i]['boundary']).bounds[0:2]] + \
                                                    [Polygon(mdir_wall[i]['boundary']).bounds[2:]]

    def my(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                value_array = np.array(mdir_wall[i]['boundary'])
                value_array[:, 0] = -value_array[:, 0] + 2 * mdir_wall[i]['boundary_centroid'][0]
                value = np.round(value_array, 1).tolist()
                mdir_wall[i]['boundary'] = value
                for j in range(3):
                    value = mdir_wall[i][modules_keys[j]][0]
                    value = round(2 * mdir_wall[i]['boundary_centroid'][0] - value, 1)
                    mdir_wall[i][modules_keys[j]][0] = value
                mdir_wall[i]['flag'] = 6
                mdir_wall[i]['module_max_bounds'] = [Polygon(mdir_wall[i]['boundary']).bounds[0:2]] + \
                                                    [Polygon(mdir_wall[i]['boundary']).bounds[2:]]

    def myr90(self, mdir_wall, mdir_num):
        for i in range(module_sum):
            if mdir_wall[i]['module_name'] == mdir_num:
                mdir_wall[i] = copy.deepcopy(self.modules_dir[mdir_num - 1])
                self.my(mdir_wall, mdir_num)
                self.r90(mdir_wall, mdir_num)
                mdir_wall[i]['flag'] = 7
                mdir_wall[i]['module_max_bounds'] = [Polygon(mdir_wall[i]['boundary']).bounds[0:2]] + \
                                                    [Polygon(mdir_wall[i]['boundary']).bounds[2:]]

    def get_port_dis(self, mdir_wall):
        dis_sum = 0
        for i in range(len(module_link)):  # 有几个连接关系就循环几次
            point_list = []
            for j in range(len(module_link[i][0])):
                for k in range(module_sum):
                    if mdir_wall[k]['module_name'] == module_link[i][0][j]:
                        point_list.append(mdir_wall[k]['port' + f'{module_link[i][1][j]}' + '_centroid'])
            value = []
            for k in range(len(point_list)):
                dis = 0
                m_copy = point_list.copy()
                xy_0 = np.array(m_copy[k])
                del m_copy[k]
                for i in range(len(point_list) - 1):
                    xy = np.array(m_copy)
                    f = (xy - xy_0) ** 2
                    f = np.round((f[:, 0] + f[:, 1]) ** (1 / 2), 1).tolist()
                    idx = f.index(min(f))
                    xy_0 = m_copy[idx]
                    # p = m_copy[idx]
                    dis += min(f)
                    del m_copy[idx]
                value.append(round(dis, 1))
            dis_sum += min(value)
        return dis_sum

    def trans_max_poly(self, mdir_wall, m_num):
        rec_lt_point = [mdir_wall[m_num - 1]['module_max_bounds'][0][0],
                        mdir_wall[m_num - 1]['module_max_bounds'][1][1]]
        rec_rb_point = [mdir_wall[m_num - 1]['module_max_bounds'][1][0],
                        mdir_wall[m_num - 1]['module_max_bounds'][0][1]]
        module_max_rec = Polygon([mdir_wall[m_num - 1]['module_max_bounds'][0], rec_lt_point,
                                  mdir_wall[m_num - 1]['module_max_bounds'][1], rec_rb_point])
        return module_max_rec

    def rec_init(self):  # 多边形外扩
        for i in range(module_sum):
            x_len = self.modules_dir[i]['module_max_bounds'][1][0] - self.modules_dir[i]['module_max_bounds'][0][0]
            y_len = self.modules_dir[i]['module_max_bounds'][1][1] - self.modules_dir[i]['module_max_bounds'][0][1]
            xf = self.wall / x_len + 1
            yf = self.wall / y_len + 1
            poly = Polygon(self.modules_dir[i]['boundary'])
            poly_list = shapely.affinity.scale(poly, xfact=xf, yfact=yf, origin='centroid').exterior.coords[:]
            poly_list = np.round(np.array(poly_list), 1).tolist()
            self.modules_dir[i]['boundary'] = poly_list
            value = np.array(self.modules_dir[i]['boundary'])
            value_average = np.round(np.average(value, axis=0), 1).tolist()
            self.modules_dir[i]['boundary_centroid'] = value_average
            self.modules_dir[i]['module_max_bounds'] = [Polygon(self.modules_dir[i]['boundary']).bounds[0:2]] + \
                                                       [Polygon(self.modules_dir[i]['boundary']).bounds[2:]]

    def move_bl(self, mdir_wall):  # BL策略移动模块改进版
        for i in range(module_sum):
            if i == 0:
                # 下述代码执行一次
                self.point_list = []
                self.m_false = 0

                self.move_init_lb(mdir_wall, 1)
                max_poly_list = self.trans_max_poly(mdir_wall, 1).exterior.coords[:]
                for p in area_poly:
                    self.point_list.append(p)
                self.point_list.append(max_poly_list[1])
                self.point_list.append(max_poly_list[2])
                self.point_list.append(max_poly_list[3])
                self.poly_union = self.trans_max_poly(mdir_wall, 1)
                continue
            self.move_start(mdir_wall, i + 1)

    def move_start(self, mdir_wall, m_num):
        for i in range(len(self.point_list)):
            point_array = np.array(self.point_list[i])
            m_rec_rb = np.array(mdir_wall[m_num - 1]['module_max_bounds'][0])
            d = point_array - m_rec_rb
            self.move(mdir_wall, m_num, xy_array=d)
            if not self.conflict(mdir_wall, m_num):
                self.move_to(mdir_wall, m_num, idx=i)
                self.poly_union = shapely.ops.unary_union((self.poly_new, self.poly_union))
                del self.point_list[i]
                self.point_list.append([mdir_wall[m_num - 1]['module_max_bounds'][0][0],
                                        mdir_wall[m_num - 1]['module_max_bounds'][1][1]])
                self.point_list.append([mdir_wall[m_num - 1]['module_max_bounds'][1][0],
                                        mdir_wall[m_num - 1]['module_max_bounds'][1][1]])
                self.point_list.append([mdir_wall[m_num - 1]['module_max_bounds'][1][0],
                                        mdir_wall[m_num - 1]['module_max_bounds'][0][1]])
                break
            else:
                if i == len(self.point_list) - 1:
                    self.m_false += 1
                    xy_p = np.array(area_zb[0]) - 1000
                    self.move_to(mdir_wall, m_num, xy=xy_p)

    def move_init_lb(self, mdir_wall, m_num):  # 模块初始化移到左下角
        xy_0 = np.array(area_rec[0])
        dis = (np.array(area_poly) - xy_0) ** 2
        dis = dis.tolist()
        dis_min_idx = dis.index(min(dis))
        area_rec_rb = area_poly[dis_min_idx]
        m_rec_rb = np.array([mdir_wall[m_num - 1]['module_max_bounds'][0][0],
                             mdir_wall[m_num - 1]['module_max_bounds'][0][1]])
        d = area_rec_rb - m_rec_rb
        self.move(mdir_wall, m_num, xy_array=d)

    def move(self, mdir_wall, m_num, x=0, y=0, xy_array=None):  # 平移函数
        # value_array = np.array(mdir_wall[m_num - 1]['boundary'])
        if xy_array is None:
            xy = np.array([x, y])
            value_array = np.array(mdir_wall[m_num - 1]['boundary'])
            mdir_wall[m_num - 1]['boundary'] = (np.round(value_array + xy, 1)).tolist()
            value_array = np.array(mdir_wall[m_num - 1]['boundary_centroid'])
            mdir_wall[m_num - 1]['boundary_centroid'] = (np.round(value_array + xy, 1)).tolist()
            for i in range(len(modules_keys)):
                value_array = np.array(mdir_wall[m_num - 1][modules_keys[i]])
                mdir_wall[m_num - 1][modules_keys[i]] = (np.round(value_array + xy, 1)).tolist()
            mdir_wall[m_num - 1]['module_max_bounds'] = [Polygon(mdir_wall[m_num - 1]['boundary']).bounds[0:2]] + \
                                                        [Polygon(mdir_wall[m_num - 1]['boundary']).bounds[2:]]
        else:  # 主要运行
            value_array = np.array(mdir_wall[m_num - 1]['boundary'])
            mdir_wall[m_num - 1]['boundary'] = (np.round(value_array + xy_array, 1)).tolist()
            value_array = np.array(mdir_wall[m_num - 1]['boundary_centroid'])
            mdir_wall[m_num - 1]['boundary_centroid'] = (np.round(value_array + xy_array, 1)).tolist()
            for i in range(len(modules_keys)):
                value_array = np.array(mdir_wall[m_num - 1][modules_keys[i]])
                mdir_wall[m_num - 1][modules_keys[i]] = (np.round(value_array + xy_array, 1)).tolist()
            mdir_wall[m_num - 1]['module_max_bounds'] = [Polygon(mdir_wall[m_num - 1]['boundary']).bounds[0:2]] + \
                                                        [Polygon(mdir_wall[m_num - 1]['boundary']).bounds[2:]]

    def move_to(self, mdir_wall, m_num, idx=0, xy=None):  # 根据模块左下角点移动到
        if xy is None:
            point_array = np.array(self.point_list[idx])
            m_rec_rb = np.array([mdir_wall[m_num - 1]['module_max_bounds'][0][0],
                                 mdir_wall[m_num - 1]['module_max_bounds'][0][1]])
            d = point_array - m_rec_rb
            self.move(mdir_wall, m_num, xy_array=d)
        else:
            m_rec_rb = np.array([mdir_wall[m_num - 1]['module_max_bounds'][0][0],
                                 mdir_wall[m_num - 1]['module_max_bounds'][0][1]])
            d = xy - m_rec_rb
            self.move(mdir_wall, m_num, xy_array=d)

    def conflict(self, mdir_wall, m_num):  # 重叠检测
        self.poly_new = self.trans_max_poly(mdir_wall, m_num)
        poly_union = shapely.ops.unary_union((self.poly_new, self.area_poly))
        poly_union_area = shapely.ops.unary_union((self.poly_new, self.poly_union)).area
        poly_union_area_new = self.poly_union.area + self.poly_new.area
        if round(poly_union.area, 1) != round(self.area_poly.area, 1):
            return True
        elif round(poly_union_area, 1) != round(poly_union_area_new, 1):
            return True
        else:
            return False

    def get_group_value(self, group_module):
        self.move_bl(group_module)
        self.group_value.append(round(self.get_port_dis(group_module), 1))

    def output(self, best_mdir):
        orient = ["R0", "R90", "R180", "R270", "MX", "MXR90", "MY", "MYR90"]
        file_path = file_out
        file = open(file_path, mode='w')
        for i in range(module_sum):
            file.write('Module: M')
            file.write(str(best_mdir[i]['module_name']))
            file.write('\n')  # 将回车写入txt文件中
            random.seed()

            file.write('Orient: ')
            file.write(str(orient[best_mdir[i]['flag']]))
            file.write('\n')  # 将回车写入txt文件中

            file.write('Offset: ')
            file.write('(')
            file.write(str(best_mdir[i]['boundary_centroid'][0]))
            file.write(',')
            file.write(str(best_mdir[i]['boundary_centroid'][1]))
            file.write(')')
            file.write('\n')  # 将回车写入txt文件中

        file.close()

    def rec_re(self):  # 多边形外扩还原
        for i in range(module_sum):
            x_len = self.group_best[i]['module_max_bounds'][1][0] - self.group_best[i]['module_max_bounds'][0][0]
            y_len = self.group_best[i]['module_max_bounds'][1][1] - self.group_best[i]['module_max_bounds'][0][1]
            xf = 1 - self.wall / x_len
            yf = 1 - self.wall / y_len
            poly = Polygon(self.group_best[i]['boundary'])
            poly_list = shapely.affinity.scale(poly, xfact=xf, yfact=yf, origin='centroid').exterior.coords[:]
            poly_list = np.round(np.array(poly_list), 1).tolist()
            self.group_best[i]['boundary'] = poly_list
            value = np.array(self.group_best[i]['boundary'])
            value_average = np.round(np.average(value, axis=0), 1).tolist()
            self.group_best[i]['boundary_centroid'] = value_average
            self.group_best[i]['module_max_bounds'] = [Polygon(self.group_best[i]['boundary']).bounds[0:2]] + \
                                                      [Polygon(self.group_best[i]['boundary']).bounds[2:]]

    def show_port(self):

        for j in range(module_sum):
            for k in range(len(modules_keys)):
                plt.scatter(x=self.group_best[j][modules_keys[k]][0], y=self.group_best[j][modules_keys[k]][1],
                            s=20, c='b', marker='x')
        for i1 in range(module_sum):
            plt.annotate(self.group_best[i1]['module_name'],
                         (self.group_best[i1]['boundary_centroid'][0],
                          self.group_best[i1]['boundary_centroid'][1]))

            plt.annotate('p1',
                         (self.group_best[i1]['port1_centroid'][0], self.group_best[i1]['port1_centroid'][1]))
            plt.annotate('p3',
                         (self.group_best[i1]['port3_centroid'][0], self.group_best[i1]['port3_centroid'][1]))

    def run(self):
        print('程序开始运行！')
        sys.stdout.write('\r程序运行中： 0 %')
        start = time.perf_counter()

        for i in range(self.M):
            self.get_group_value(self.group[i])  # 个体操作

        end = time.perf_counter()

        loc = end - start

        while 1:

            idx = self.group_value.index(min(self.group_value))
            self.group_best = self.group[idx]

            self.crossover(idx)  # 个体交叉
            self.renew_group()  # 更新群体

            idx = self.group_value.index(min(self.group_value))
            self.group_best = self.group[idx]

            end = time.perf_counter()
            if self.t == 0:
                pre = (end - start) - loc

            per_time = (end - start) / 300 * 100
            sys.stdout.write(f'\r程序运行中： {per_time:.0f} %')

            if (end - start) + pre >= 290:
                break

            self.mutation(idx)  # 个体变异
            self.renew_group()  # 更新群体

            idx = self.group_value.index(min(self.group_value))
            self.group_best = self.group[idx]

            end = time.perf_counter()

            per_time = (end - start) / 300 * 100
            sys.stdout.write(f'\r程序运行中： {per_time:.0f} %')

            if (end - start) + pre >= 290:
                break

            self.t += 1  # 群体代数+1

        end = time.perf_counter()

        sys.stdout.write('\r程序运行中： 100 %')

        self.rec_re()  # 多边形外扩还原
        self.output(self.group_best)

        self.show_port()  # 标记出端口

        draw_module(self.group_best, a=1)
        print('\n程序运行结束！\n成功生成 result.txt\n成功生成 result.jpg')
        print(f'程序运行耗时： {end - start:.1f} s')


def run_ga():
    test = GA_SA(modules, area_poly)
    test.run()


if __name__ == '__main__':
    random.seed(0)
    for i in range(10004, 10005):
        try:
            global module_idx
            module_idx = f'20-{i}'
            # module_idx = f'16-{i}'
            global file_Module
            file_Module = rf'.\sample\{module_idx[:2]}ModuleCase\{module_idx}\Module.txt'
            global file_connect_1
            file_connect_1 = rf'.\sample\{module_idx[:2]}ModuleCase\{module_idx}\connect_1.txt'
            global file_out
            file_out = temp = os.getcwd() + rf'\sample\result\result_{module_idx}.txt'
            # file_out = rf'D:\PycharmProjects\hd5\sample\result\seed_0\result_{module_idx}.txt'
            global list_keys
            list_keys = ['boundary', 'port1', 'port2', 'port3']
            global modules_keys
            modules_keys = ['port1_centroid', 'port2_centroid', 'port3_centroid']
            global module_link
            module_link = []
            with_open()
        except FileNotFoundError:
            continue
        else:
            print(f'当前模块为 {module_idx}')
            run_ga()
            plt.close('all')
